﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Web.Routing;
using System.Reflection;
using Microsoft.Web.Mvc;

namespace System.Web.Mvc
{
	// Quote from MVC2RC
	public static partial class ExpressionHelper2
	{
		public static string GetExpressionText(this string expression)
		{
			return
				String.Equals(expression, "model", StringComparison.OrdinalIgnoreCase)
					? String.Empty    // If it's exactly "model", then give them an empty string, to replicate the lambda behavior
					: expression;
		}

		public static string GetExpressionText(this LambdaExpression expression)
		{
			// Crack the expression string for property/field accessors to create its name
			Stack<string> nameParts = new Stack<string>();
			Expression part = expression.Body;

            while (part != null)
            {
                if (part.NodeType != System.Linq.Expressions.ExpressionType.MemberAccess)
                    break;
                MemberExpression memberExpressionPart = (MemberExpression)part;
                nameParts.Push(memberExpressionPart.Member.Name);
                part = memberExpressionPart.Expression;
            }

			// If it starts with "model", then strip that away
			if (nameParts.Count > 0 && String.Equals(nameParts.Peek(), "model", StringComparison.OrdinalIgnoreCase))
				nameParts.Pop();
			if (nameParts.Count > 0)
				return nameParts.Aggregate((left, right) => left + "." + right);
			return String.Empty;
		}
	}

	// Quote from MvcFutures of MVC2RC
	public static partial class ExpressionHelper2
	{
		public static RouteValueDictionary GetRouteValues<TController>
            (this Expression<Action<TController>> action) where TController : Controller
		{
			if (action == null)
				throw new ArgumentNullException("action");
			MethodCallExpression call = action.Body as MethodCallExpression;
			if (call == null)
				throw new ArgumentException("Expression must be a method call.", "action");
			string controllerName = typeof(TController).Name;
			if (!controllerName.EndsWith("Controller", StringComparison.OrdinalIgnoreCase))
				throw new ArgumentException("Controller name must end in 'Controller'.", "action");
			controllerName = controllerName.Substring(0, controllerName.Length - "Controller".Length);
			if (controllerName.Length == 0)
				throw new ArgumentException("Cannot route to class named 'Controller'.", "action");

			// TODO: How do we know that this method is even web callable?
			//      For now, we just let the call itself throw an exception.

			var rvd = new RouteValueDictionary();
			rvd.Add("Controller", controllerName);
			rvd.Add("Action", call.Method.Name);
			AddParameterValuesFromExpressionToDictionary(rvd, call);
			return rvd;
		}
		private static void AddParameterValuesFromExpressionToDictionary(RouteValueDictionary rvd, MethodCallExpression call)
		{
			ParameterInfo[] parameters = call.Method.GetParameters();

			if (parameters.Length > 0)
			{
				for (int i = 0; i < parameters.Length; i++)
				{
					Expression arg = call.Arguments[i];
					object value = null;
					ConstantExpression ce = arg as ConstantExpression;
					if (ce != null)
					{
						// If argument is a constant expression, just get the value
						value = ce.Value;
					}
					else
					{
						value = CachedExpressionCompiler.Evaluate(arg);
					}
					rvd.Add(parameters[i].Name, value);
				}
			}
		}
	}
}
